package com.softtek.ibeacon.dao;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import com.softtek.ibeacon.common.ReflectionUtils;
import com.softtek.ibeacon.common.StringUtils;

public class DAO extends JdbcTemplate {

	NamedParameterJdbcTemplate npt = null;
	private static Logger logger = LoggerFactory.getLogger(DAO.class);

	public NamedParameterJdbcTemplate getNpt() {
		if (npt == null) {
			npt = new NamedParameterJdbcTemplate(this.getDataSource());
		}
		return npt;
	}

	@Override
	public DataSource getDataSource() {

		DataSource d = super.getDataSource();
		return d;
	}

 	public String save(Object entity) {
		String tableName = this.getTableName(entity.getClass());
		Map<String, String> map = getColumns(entity.getClass());
		Set<String> set = map.keySet();
		Map<String, Object> m2 = new HashMap<String, Object>();
		for (String key : set) {
			Object obj = ReflectionUtils.getFieldValue(entity, key);
			if (obj != null && obj.getClass().getPackage().getName().indexOf("entity") > 1) {
				m2.put(map.get(key).toString(), ReflectionUtils.getFieldValue(obj, getPk(obj.getClass()).keySet().toArray()[0] + ""));
			} else {
				m2.put(map.get(key).toString(), obj);
			}
		}
		SimpleJdbcInsert insert = new SimpleJdbcInsert(this);
		insert.withTableName(tableName);

		String pk = "";

		Map<String, ?> pkMap = getPk(entity.getClass());

		String rtn = "";
		if (pkMap == null || pkMap.isEmpty()) {
			// 如无主键直接插入数据
			insert.execute(m2);
		} else {
			String[] pks = new String[pkMap.keySet().size()];
			int i = 0;
			for (String s : pkMap.keySet()) {
				pks[i] = s;
				i++;
			}
			if (pks.length == 1) {
				pk = pks[0];
				try {
					Method pkMethod = entity.getClass().getMethod(pk);
					System.out.println(pkMethod);
					// 判断是否有主键生成策略
					if (pkMethod.isAnnotationPresent(GeneratedValue.class)) {
						insert.setGeneratedKeyName(StringUtils.replaceGet(pk));
						Number id = insert.executeAndReturnKey(m2);
						rtn = id.toString();
					} else {
						insert.execute(m2);
						rtn = String.valueOf(m2.get(pk));
					}

				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {
				// 多主键时直接插入
				insert.execute(m2);
			}
		}
		return rtn;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * #delete(java.lang.Object)
	 */
	public int delete(Object entity) {
		Map pkmap = getPk(entity.getClass());
		String tableName = this.getTableName(entity.getClass());
		StringBuffer sql = new StringBuffer("delete from " + tableName + " where ");
		Map<String, Object> m = new HashMap<String, Object>();
		String s = "";
		String s2 = "";
		Set<String> pkset = pkmap.keySet();
		for (String key : pkset) {
			s2 += pkmap.get(key) + " = :" + pkmap.get(key) + " and ";
			m.put(pkmap.get(key) + "", ReflectionUtils.getFieldValue(entity, key));
		}
		sql.append(s2.substring(0, s2.length() - 4));
		return getNpt().update(sql.toString(), m);
	}

	public int[] batchDelete(Class cls, final String[] pks) {
		Map pkmap = getPk(cls);
		String tableName = this.getTableName(cls);
		StringBuffer sql = new StringBuffer("delete from " + tableName + " where ");
		Map<String, Object> m = new HashMap<String, Object>();
		String s2 = "";
		Set<String> pkset = pkmap.keySet();
		for (String key : pkset) {
			s2 += pkmap.get(key) + " = ? and ";
		}
		sql.append(s2.substring(0, s2.length() - 4));
		List param = new ArrayList();
		for (int i = 0; i < pks.length; i++) {
			String[] o = new String[] { pks[i] };
			param.add(o);
		}
		return this.batchUpdate(sql.toString(), param);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * #update(java.lang.Object)
	 */
	public int update(Object entity) {
		String tableName = this.getTableName(entity.getClass());
		Map map = this.getColumns(entity.getClass());
		StringBuffer sql = new StringBuffer("UPDATE " + tableName + " set ");
		Set<String> set = map.keySet();
		Map<String, Object> m = new HashMap<String, Object>();
		String s = "";
		String s2 = "";
		int count = set.size();
		for (String key : set) {
			s2 += map.get(key) + "= :" + map.get(key) + ",";
			m.put(map.get(key) + "", ReflectionUtils.getFieldValue(entity, key));
		}
		sql.append(s2.substring(0, s2.length() - 1));
		sql.append(" where ");
		Map pkmap = getPk(entity.getClass());
		Set<String> pkset = pkmap.keySet();
		for (String key : pkset) {
			s += pkmap.get(key) + " = :" + pkmap.get(key) + " and ";
			m.put(pkmap.get(key) + "", ReflectionUtils.getFieldValue(entity, key));
		}
		sql.append(s.substring(0, s.length() - 4));
		return getNpt().update(sql.toString(), m);
	}

	// 当字段值为空时不编辑
	public int update2(Object entity) {
		Class<?> entityClass = entity.getClass();
		String tableName = this.getTableName(entityClass);
		Map<String, String> columnMap = getColumns(entityClass);
		StringBuffer sql = new StringBuffer("UPDATE " + tableName + " SET ");
		Map<String, Object> m = new HashMap<String, Object>();
		String whereSql = "";
		String setSql = "";
		for (String key : columnMap.keySet()) {
			Object value = ReflectionUtils.getFieldValue(entity, key);
			if (null != value) {
				String column = columnMap.get(key);
				setSql += column + "= :" + column + ",";
				m.put(column + "", value);
			}
		}
		sql.append(setSql.substring(0, setSql.length() - 1));
		sql.append(" WHERE ");
		Map<String, String> pkmap = getPk(entityClass);
		for (String key : pkmap.keySet()) {
			String pk = pkmap.get(key);
			whereSql += pk + " = :" + pk + " AND ";
			m.put(pk + "", ReflectionUtils.getFieldValue(entity, key));
		}
		sql.append(whereSql.substring(0, whereSql.length() - 4));
		return getNpt().update(sql.toString(), m);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * #executeSave(java.lang.String,
	 *      java.lang.Object)
	 */
	public Object executeSave(String sql, Object entity) {
		SqlParameterSource param = new BeanPropertySqlParameterSource(entity);
		KeyHolder keyholder = new GeneratedKeyHolder();
		getNpt().update(sql, param, keyholder);
		return keyholder.getKey().intValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * #executeUpdate(java.lang.String,
	 *      java.lang.Object)
	 */
	public Object executeUpdate(String sql, Object entity) {
		SqlParameterSource param = new BeanPropertySqlParameterSource(entity);
		return getNpt().update(sql, param);
	}

	public Map<String, ?> queryByClass2Entity(Class entity, int firstResult, int maxResults) {
		String sql = "SELECT * FROM " + this.getTableName(entity);
		return queryBySql2Entity(sql, null, entity, firstResult, maxResults);
	}

	public Map<String, ?> queryByClass2Map(Class entity, int firstResult, int maxResults) {
		String sql = "SELECT * FROM " + this.getTableName(entity);
		return queryBySql2Map(sql, null, firstResult, maxResults);
	}

 
	
	public Map<String, ?> queryBySql2Entity(String sql, Object[] args, Class entity, int firstResult, int maxResults) {
		Map map = new HashMap();
		map.put("total", queryForLong("SELECT COUNT(0) FROM (" + sql + ") A", args));

		String limitSql = "SELECT * FROM (" + sql + ") _A ";
		if (maxResults > 50) {
			logger.warn("查询每页超过50条!" + limitSql);
		}
		if (maxResults > 0) {
			limitSql += "LIMIT " + firstResult + "," + maxResults;
		}

		map.put("rows", query(limitSql, new BeanPropertyRowMapper(entity), args));
		return map;
	}

	public Map<String, ?> queryBySql2Map(String sql, Object[] args, int firstResult, int maxResults) {
		Map map = new HashMap();
		map.put("total", queryForLong("SELECT COUNT(0) FROM (" + sql + ") A", args));
		String limitSql = "SELECT * FROM (" + sql + ") _A ";
		if (maxResults > 0) {
			limitSql += "LIMIT " + firstResult + "," + maxResults;
		}
		map.put("rows", queryForList(limitSql, args));
		return map;
	}

	public Object findById(Class entityClass, Serializable id) {
		Map pkmap = getPk(entityClass);
		String tableName = this.getTableName(entityClass);

		StringBuffer sql = new StringBuffer("select * from " + tableName + " where ");
		Map<String, Object> m = new HashMap<String, Object>();
		String s2 = "";
		Set<String> pkset = pkmap.keySet();
		for (String key : pkset) {
			s2 += pkmap.get(key) + " = :" + pkmap.get(key) + " and ";
			m.put(pkmap.get(key) + "", id);
		}
		sql.append(s2.substring(0, s2.length() - 4));
		RowMapper rowMapper = new BeanPropertyRowMapper(entityClass);
		try {
			return getNpt().queryForObject(sql.toString(), m, rowMapper);
		} catch (Exception e) {
			return null;
		}
	}

	public Map findById2Map(Class entityClass, Serializable id) {
		Map pkmap = getPk(entityClass);
		String tableName = this.getTableName(entityClass);
		StringBuffer sql = new StringBuffer("select * from " + tableName + " where ");
		Map<String, Object> m = new HashMap<String, Object>();
		String s2 = "";
		Set<String> pkset = pkmap.keySet();
		for (String key : pkset) {
			s2 += pkmap.get(key) + " = :" + pkmap.get(key) + " and ";
			m.put(pkmap.get(key) + "", id);
		}
		sql.append(s2.substring(0, s2.length() - 4));
		try {
			return getNpt().queryForMap(sql.toString(), m);
		} catch (Exception e) {
			return null;
		}

	}

	public Object findById(Object entity) {
		Map pkmap = getPk(entity.getClass());
		String tableName = this.getTableName(entity.getClass());

		StringBuffer sql = new StringBuffer("select * from " + tableName + " where ");
		Map<String, Object> m = new HashMap<String, Object>();
		String s = "";
		String s2 = "";
		Set<String> pkset = pkmap.keySet();
		for (String key : pkset) {
			s2 += pkmap.get(key) + " = :" + pkmap.get(key) + " and ";
			m.put(pkmap.get(key) + "", ReflectionUtils.getFieldValue(entity, key));
		}
		sql.append(s2.substring(0, s2.length() - 4));
		RowMapper rowMapper = new BeanPropertyRowMapper(entity.getClass());
		try {
			return getNpt().queryForObject(sql.toString(), m, rowMapper);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage());
			return null;
		}

	}

	@SuppressWarnings(value = { "rawtypes" })
	public static Map<String, String> getColumns(Class classtype) {
		Method[] methods = classtype.getDeclaredMethods();
		Map<String, String> map = new HashMap<String, String>();
		for (int i = 0; i < methods.length; i++) {
			Annotation[] annotations = methods[i].getAnnotations();
			for (int j = 0; j < annotations.length; j++) {
				if (annotations[j] instanceof Column) {
					Column column = (Column) annotations[j];
					map.put(StringUtils.replaceGet(methods[i].getName()), column.name());
				} else if (annotations[j] instanceof JoinColumn) {
					JoinColumn column = (JoinColumn) annotations[j];
					map.put(StringUtils.replaceGet(methods[i].getName()), column.name());
				}
			}
		}
		Class superClass = classtype.getSuperclass();
		if (superClass != null) {
			methods = superClass.getDeclaredMethods();
			for (int i = 0; i < methods.length; i++) {
				Annotation[] annotations = methods[i].getAnnotations();
				for (int j = 0; j < annotations.length; j++) {
					if (annotations[j] instanceof Column) {
						Column column = (Column) annotations[j];
						map.put(StringUtils.replaceGet(methods[i].getName()), column.name());
					}
				}
			}
		}
		return map;
	}

	public static Map<String, String> getPk(Class classtype) {
		if (!classtype.getSuperclass().getName().equals("java.lang.Object")) {
			return getPk(classtype.getSuperclass());
		}

		Method[] methods = classtype.getMethods();
		Map<String, String> map = null;
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if (method.isAnnotationPresent(Id.class) && method.isAnnotationPresent(Column.class)) {
				Column column = method.getAnnotation(Column.class);
				if (map == null) {
					map = new HashMap<String, String>();
				}
				map.put(methods[i].getName(), column.name());
			}
		}
		return map;
	}

	public static String getPkColumn(Class classtype) {
		Field[] fields = classtype.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field filed = fields[i];
			if (filed.isAnnotationPresent(Id.class) && filed.isAnnotationPresent(Column.class)) {
				Column column = filed.getAnnotation(Column.class);
				return column.name();
			}
		}
		return "id";
	}

	/**
	 * 获取表名
	 * 
	 * @param classtype
	 * @return
	 */
	@SuppressWarnings(value = { "rawtypes" })
	public String getTableName(Class classtype) {
		Annotation[] anno = classtype.getAnnotations();
		String tableName = "";
		for (int i = 0; i < anno.length; i++) {
			if (anno[i] instanceof Table) {
				Table table = (Table) anno[i];
				tableName = table.name();
			}
		}
		return tableName;
	}

}
